package cz.boxdesign.library.utils;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class SetCharacterEncodingFilter implements Filter {

  private static final Log LOG = LogFactory.getLog(SetCharacterEncodingFilter.class);

  /**
   * The default character encoding to set for requests that pass through
   * this filter.
   */
  private String encoding = null;

  /**
   * Should a character encoding specified by the client be ignored?
   */
  private boolean ignoreClientEncoding = false;

  /**
   * {@inheritDoc}
   * <p>
   * <b>Specific implmentation details:</b>
   * <p>
   * Releases instanciated class objects.
   */
  @Override
  public void destroy() {
    this.encoding = null;
  }

  /**
   * {@inheritDoc}
   * <p>
   * <b>Specific implmentation details:</b>
   * <p>
   * Sets the character encoding to be used in parsing the incoming request, either unconditionally or only if the client did not specify a
   * character encoding.
   */
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    try {
      if (this.ignoreClientEncoding || null == request.getCharacterEncoding()) {
        request.setCharacterEncoding(this.encoding);
        response.setCharacterEncoding(this.encoding);
      }
    } catch (Exception e) {
      LOG.error("Ignoring catched exception while setting the request/response character encoding: cause: " + e.getMessage(), e);
    } finally {
      // call next element in chain in each case
      chain.doFilter(request, response);
    }
  }

  /**
   * {@inheritDoc}
   * <p>
   * <b>Specific implmentation details:</b>
   * <p>
   * Initializes the <code>encoding</code> attribute of the filter with value-validation. An validation error is wrapped into the
   * ServletException, see Filter API doc.
   * <p>
   * The filter behaviour is based on the following servlet filter initialization parameter, see web.xml:
   * </p>
   * <strong>encoding</strong> - The character encoding to be configured
   * for this request. This parameter is required, so there is no default. <br/>
   * <strong>ignoreClientEncoding</strong> - If set to "true", any character encoding
   * specified by the client is ignored, and the value of <strong>encoding</strong> is set. If set to "false,
   * only if the client has not already specified an encoding. By default, this
   * parameter is set to "false".
   * <p>
   */
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    try {
      // get configured "encoding"
      this.encoding = filterConfig.getInitParameter("encoding");
      // test passed value, in error case throws an UnsupportedEncodingException or NullPointerException
      "".getBytes(this.encoding);

      // get configured encoding condition
      if ("true".equalsIgnoreCase(filterConfig.getInitParameter("ignoreClientEncoding"))) {
        this.ignoreClientEncoding = true;
      }

      LOG.info("SetCharacterEncodingFilter: set the character encoding to " + this.encoding + " with ignoreClientEncoding="
          + this.ignoreClientEncoding);
    } catch (Exception e) {
      // catch the Exception to wrap it into the ServletException, see Filter API
      LOG.error("An error occurs while initializing the servlet filter 'SetCharacterEncodingFilter': cause: " + e.getMessage(), e);
      throw new ServletException("Nested exception in SetCharacterEncodingFilter::init(FilterConfig) occurs: " + e.getMessage(), e);
    }
  }
}
